package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"zinx/ziface"
)

type Connection struct {
	//这里放置属性
	//当前连接的socket TCP套接字
	Conn *net.TCPConn
	//链接ID
	ConnID uint32
	//链接状态
	isClosed bool
	// //处理业务方法API
	// handleAPI ziface.HandleFunc
	//该连接的处理方法router
	// Router ziface.IRouter

	//消息的管理MsgId和对应的处理业务API的关系
	MsgHandler ziface.IMsgHandle
	//告知当前连接退出/停止的channel
	ExitChan chan bool
	//无缓冲管道，用于读、写两个goroutine之间的消息通信
	msgChan chan []byte
}

//初始化连接模块

func NewConntion(conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
	c := &Connection{
		//初始化Conn属性
		Conn:     conn,
		ConnID:   connID,
		isClosed: false,
		// Router:   router,
		MsgHandler: msgHandler,
		ExitChan:   make(chan bool, 1), //创建channel的格式如下：make(chan 元素类型, [缓冲大小])
		msgChan:    make(chan []byte),  //msgChan初始化

	}
	return c
}

//写消息的goroutine，专门将消息发送给客户端的模块。
func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn Writer exit!]")
	//不断地阻塞的等待channel的消息
	/*
			可处理一个或多个channel的发送/接收操作。
		如果多个case同时满足，select会随机选择一个。
		对于没有case的select{}会一直等待，可用于阻塞main函数。
	*/
	for {
		select {
		case data := <-c.msgChan:
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send Data error:, ", err, " Conn Writer exit")
				return
			}
		case <-c.ExitChan: //如果可读取了，就说明退出了。 //得到退出消息，不再阻塞
			//conn已经关闭
			return
		}
	}
}
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is  running")
	defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!")
	//都结束了，当然也就要stop了
	defer c.Stop()

	//reader读不到信息了，就跳出这个for循环，执行c.Stop()，在stop中可以做一些资源回收等工作

	//读客户端数据，最大的字节数
	for {
		/*
			// buf := make([]byte, 512)
			buf := make([]byte, 515)
			_, err := c.Conn.Read(buf)
			if err != nil {
				fmt.Println("recv buf err ", err)
				continue //cotinue是为了继续读，而不是直接跳出循环
			}
		*/

		// 创建一个拆包解包对象
		dp := NewDataPack()

		//读取客户端Msg Head的二进制流8 个字节
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error ", err)
			break
		}
		//拆包，得到msgid 和 datalen 放在msg中
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error ", err)
			break
		}
		//根据 dataLen 读取 data，放在msg.Data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data error ", err)
				break
			}
		}
		msg.SetData(data) //设计消息内容
		// //调用API            有router了，这里不需要了
		// if err := c.handleAPI(c.Conn,buf,cnt); err!=nil{
		// 	fmt.fmt.Println("ConnID ",c.ConnID,"handle is error",err)

		//打到当前的request请求数据
		req := Request{
			conn: c,
			msg:  msg,
		}
		// //从路由Routers 中找到注册绑定Conn的对应Handle
		// go func(request ziface.IRequest) {
		// 	//执行注册的路由方法
		// 	c.Router.PreHandle(request)
		// 	c.Router.Handle(request)
		// 	c.Router.PostHandle(request)
		// }(&req)
		//根据绑定好的id找到对应的处理api的业务，执行
		go c.MsgHandler.DoMsgHandler(&req)
	}

}

func (c *Connection) Start() {
	fmt.Println("Conn Start()... ConnID=", c.ConnID)
	//两个goroutine，分别处理读、写
	//1 开启用户从客户端读取数据流程的Goroutine
	go c.StartReader()
	//2 开启用于写回客户端数据流程的Goroutine
	go c.StartWriter()
	//当reader得到关闭信息时，就退出
	for {
		select {
		case <-c.ExitChan:
			//得到退出消息，不再阻塞
			return
		}
	}
}

func (c *Connection) Stop() {
	fmt.Println("Conn Stop()...ConnID = ", c.ConnID)
	//如果已经关闭
	if c.isClosed == true {
		return
	}
	c.isClosed = true
	//关闭socket链接
	c.Conn.Close()
	//告知writer关闭
	c.ExitChan <- true
	//回收资源
	close(c.ExitChan)
	close(c.msgChan)
}

//从当前连接获取原始的socket TCPConn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

//获取当前连接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

//获取远程客户端地址信息
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

//提供一个sendmsg方法，将我们要发送给客户端的数据，先进行封包，再发送
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send msg")
	}
	//将data封包，并且发送
	dp := NewDataPack() //dp得到的是一个空结构体的地址
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id = ", msgId)
		return errors.New("Pack error msg ")
	}
	/*
		//将数据发送到客户端
		if _, err := c.Conn.Write(msg); err != nil {
			fmt.Println("Write msg id ", msgId, " error ")
			return errors.New("conn Write error")
		}
	*/
	//这时候不回写给客户端了，现在写入无缓冲管道
	c.msgChan <- msg //将之前直接回写给conn.Write的方法 改为 发送给Channel 供Writer读取

	return nil
}
